package jia.ai;

import jia.game.GenericFlags;
import org.apache.log4j.Logger;

import java.awt.*;
import java.util.ArrayList;
import java.util.Iterator;

public class Course{
    
    private static final Logger log = Logger.getLogger(Course.class);
    
    /**
     * @param originPoint
     * @param blockedSquares
     *
     * @return Iterator of course points
     */
    public ArrayList<Point> plot(Point originPoint, Point originalGoalPoint, Point[] blockedSquares){
        
        ArrayList<Point> pathToTarget = new ArrayList<Point>();
        try {
            //Original point of origin of the mover in the battle grid
            if(GenericFlags.debugMessages) {
                log.debug("The origin point of the mover is " + originPoint);
            }
            
            //Original point of origin of the target
            if(GenericFlags.debugMessages) {
                log.debug("His target's location is " + originalGoalPoint);
            }
            
            //since the mover is the "center of the universe", recalculate the target origin and the
            //obstacles as though the mover were sitting on square 0, 0;
            Point newGoalPoint = recalculatePoint(originPoint, originalGoalPoint);
            Point[] adjBlockedSqrs = recalculateObstacles(blockedSquares, originPoint);
            
            //with the new values, plot a course.
            AStarData dataForPath = new AStarData(newGoalPoint, adjBlockedSqrs, originPoint);
            
            //AStarData.test(dataForPath);
            AStar aStar = new AStar(dataForPath);
            aStar.reset(new Point(0, 0));
            while(true) {
                aStar.loop();
                if(aStar.isListEmpty()) {
                    break;
                }
            }
            if(aStar.isGoalFound()) {
                if(GenericFlags.debugMessages) {
                    log.debug("Found Destination!");
                }
            }
            
            //now that we have a "course", move the mover to the correct squares as though the adjustments
            //had never been made.
            
            //loop through the iterator of plot points
            Iterator iterator = aStar.getPath();
            ArrayList<Point> convertedList = new ArrayList<Point>();
            while(iterator.hasNext()) {
                convertedList.add(translateAdjPointToOriginal(originPoint, (Point) iterator.next()));
            }
            pathToTarget = convertedList;
            if (GenericFlags.debugMessages){
                log.debug("The arrayList has a length of " + convertedList.size());
            }
        } catch(Exception e) {
            if(GenericFlags.errorMessages) {
                log.error("Could not plot a course! " + e);
            }
        }
        return pathToTarget;
    }
    
    /**
     * @param originPoint
     * @param blockedSquares
     *
     * @return
     */
    public ArrayList<Point> plotRetreat(Point originPoint, Point[] blockedSquares){
        ArrayList<Point> pathAwayFromTarget = new ArrayList<Point>();
        Point goalPoint = new Point(0, 0);
        try {
            //Original point of origin of the mover in the battle grid
            if(GenericFlags.debugMessages) {
                log.debug("The origin point of the mover is " + originPoint);
            }
            
            //Original point of origin of the target
            if(GenericFlags.debugMessages) {
                log.debug("His target's location is " + goalPoint);
            }
            
            //since the mover is the "center of the universe", recalculate the target origin and the
            //obstacles as though the mover were sitting on square 0, 0;
            Point newGoalPoint = recalculatePoint(originPoint, goalPoint);
            Point[] adjBlockedSqrs = recalculateObstacles(blockedSquares, originPoint);
            
            //with the new values, plot a course.
            AStarData dataForPath = new AStarData(newGoalPoint, adjBlockedSqrs, originPoint);
            //AStarData.test(dataForPath);
            AStar aStar = new AStar(dataForPath);
            aStar.reset(new Point(0, 0));
            while(true) {
                aStar.loop();
                if(aStar.isListEmpty()) {
                    break;
                }
            }
            if(aStar.isGoalFound()) {
                if(GenericFlags.debugMessages) {
                    log.debug("Found Destination!");
                }
            }
            
            //now that we have a "course", move the mover to the correct squares as though the adjustments
            //had never been made.
            
            //loop through the iterator of plot points
            Iterator iterator = aStar.getPath();
            ArrayList<Point> convertedList = new ArrayList<Point>();
            while(iterator.hasNext()) {
                convertedList.add(translateAdjPointToOriginal(originPoint, (Point) iterator.next()));
            }
            pathAwayFromTarget = convertedList;
            if (GenericFlags.debugMessages){
                log.debug("The arrayList has a length of " + convertedList.size());
            }
        } catch(Exception e) {
            if(GenericFlags.errorMessages) {
                log.error("Could not plot a course! " + e);
            }
        }
        return pathAwayFromTarget;
    }
    
    /**
     * @param originPoint
     * @param originalGoalPoint
     *
     * @return
     */
    private static Point recalculatePoint(Point originPoint, Point originalGoalPoint){
        double x;
        double y;
        Point adjPoint = new Point();
        try {
            x = originalGoalPoint.getX() - originPoint.getX();
            y = originalGoalPoint.getY() - originPoint.getY();
            adjPoint.setLocation(x, y);
        } catch(Exception e) {
            if(GenericFlags.errorMessages) {
                log.error("Error extrapolating the new goal point " + e);
            }
        }
        return adjPoint;
    }
    
    /**
     * @param obstacles
     * @param originPoint
     *
     * @return
     */
    private static Point[] recalculateObstacles(Point[] obstacles, Point originPoint){
        Point[] adjPointArry = new Point[obstacles.length];
        try {
            for(int i = 0; i < obstacles.length; i++) {
                Point obstacle = obstacles[i];
                adjPointArry[i] = recalculatePoint(originPoint, obstacle);
            }
        } catch(Exception e) {
            if(GenericFlags.errorMessages) {
                log.error("Error extrapolating the new obstacle locations " + e.getMessage());
            }
        }
        return adjPointArry;
    }
    
    /**
     * @param originPoint
     * @param plotPoint
     *
     * @return
     */
    private static Point translateAdjPointToOriginal(Point originPoint, Point plotPoint){
        Point adjPoint = new Point(0, 0);
        double X;
        double Y;
        if (GenericFlags.debugMessages){
            log.debug("The origin point is: " + originPoint);
            log.debug("The point to be converted is " + plotPoint);
        }
        try {
            X = originPoint.getX() + plotPoint.getX();
            Y = originPoint.getY() + plotPoint.getY();
            adjPoint.setLocation(X, Y);
            if (GenericFlags.debugMessages){
                log.debug("The converted point is: " + adjPoint);
            }
        } catch(Exception e) {
            if(GenericFlags.errorMessages) {
                log.error("Error returning origin point back to original value " + e.getMessage());
            }
        }
        return adjPoint;
    }
    
    /**
     *
     */
    //TODO: static test of the plot function.  Can be removed later
    private static void plot(){
        try {
            //Original point of origin of the mover in the battle grid
            Point originPoint = new Point(7, 8);
            if(GenericFlags.debugMessages) {
                log.debug("The origin point of the mover is " + originPoint);
            }
            
            //Original point of origin of the target
            Point originalGoalPoint = new Point(11, 12);
            if(GenericFlags.debugMessages) {
                log.debug("His target's location is " + originalGoalPoint);
            }
            
            //Array of squares that are blocked and impassable
            Point[] blockedSquares = {new Point(8, 4), new Point(9, 4), new Point(0, 5), new Point(1, 5), new Point(2, 5), new Point(3, 5), new Point(4, 5), new Point(5, 5), new Point(6, 5), new Point(8, 5), new Point(9, 5), new Point(0, 6), new Point(1, 6), new Point(2, 6), new Point(3, 6), new Point(4, 6), new Point(5, 6), new Point(6, 6), new Point(11, 6), new Point(12, 6), new Point(14, 6), new Point(15, 6), new Point(3, 7), new Point(8, 7), new Point(9, 7), new Point(11, 7), new Point(12, 7), new Point(14, 7), new Point(15, 7), new Point(5, 8), new Point(6, 8), new Point(8, 8), new Point(9, 8), new Point(5, 9), new Point(6, 9), new Point(11, 9), new Point(12, 9), new Point(13, 9), new Point(1, 10), new Point(2, 10), new Point(6, 10), new Point(11, 10), new Point(15, 10), new Point(1, 11), new Point(2, 11), new Point(3, 11), new Point(8, 11), new Point(10, 11), new Point(11, 11), new Point(13, 11), new Point(14, 11), new Point(15, 11), new Point(2, 12), new Point(3, 12), new Point(6, 12), new Point(7, 12), new Point(8, 12), new Point(10, 12), new Point(13, 12), new Point(14, 12), new Point(15, 12), new Point(0, 13), new Point(5, 13), new Point(6, 13), new Point(7, 13), new Point(8, 13), new Point(12, 13), new Point(13, 13), new Point(14, 13), new Point(15, 13), new Point(0, 14), new Point(1, 14), new Point(5, 14), new Point(6, 14), new Point(11, 14), new Point(12, 14), new Point(13, 14), new Point(14, 14), new Point(15, 14), new Point(0, 15), new Point(1, 15), new Point(2, 15), new Point(10, 15), new Point(11, 15), new Point(12, 15), new Point(13, 15), new Point(14, 15), new Point(15, 15)};
            
            //since the mover is the "center of the universe", recalculate the target origin and the
            //obstacles as though the mover were sitting on square 0, 0;
            Point newGoalPoint = recalculatePoint(originPoint, originalGoalPoint);
            Point[] adjBlockedSqrs = recalculateObstacles(blockedSquares, originPoint);
            
            //with the new values, plot a course.
            AStarData dataForPath = new AStarData(newGoalPoint, adjBlockedSqrs, originPoint);
            AStar aStar = new AStar(dataForPath);
            aStar.reset(new Point(0, 0));
            while(true) {
                aStar.loop();
                if(aStar.isListEmpty()) {
                    break;
                }
            }
            if(aStar.isGoalFound()) {
                if(GenericFlags.debugMessages) {
                    log.debug("Found Destination!");
                }
            }
            
            //now that we have a "course", move the mover to the correct squares as though the adjustments
            //had never been made.
            
            //loop through the iterator of plot points
            Iterator iterator = aStar.getPath();
            ArrayList<Point> convertedList = new ArrayList<Point>();
            for(Point adjBlockedSqr : convertedList) {
                if(GenericFlags.debugMessages) {
                    log.debug("Mover needs to go to: " + translateAdjPointToOriginal(originPoint, adjBlockedSqr));
                }
            }
            while(iterator.hasNext()) {
                convertedList.add(translateAdjPointToOriginal(originPoint, (Point) iterator.next()));
            }
            for(int i = 0; i < convertedList.size() - 1; i++) {
                Point adjBlockedSqr = convertedList.get(i);
                if(GenericFlags.debugMessages) {
                    log.debug("Mover needs to go to: " + adjBlockedSqr);
                }
            }
            if (GenericFlags.debugMessages){
                log.debug("The arrayList has a length of " + convertedList.size());
            }
        } catch(Exception e) {
            if(GenericFlags.errorMessages) {
                log.error("Could not plot a course! " + e);
            }
        }
    }
    
    /**
     * Test Main method that runs the test plot method
     *
     * @param args
     */
    public static void main(String[] args){
        plot();
    }
}
